﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using ClickerClimber.Models;
using ClickerClimber.Managers;
using Microsoft.Xna.Framework.Graphics;

namespace ClickerClimber.Model 
{
    public class Beaver : IGameObject, IGameObjectDrawable
    {
        private Random randomGenerator = new Random();
        private Vector2 _spritePosition;
        private Texture2D _textureItem;

        private Texture2D _line;
        private Vector2 _spriteOffset;
        private Pair _ItemPosition;
        private float _width, _height;
        private Item _item;
        int _nbClicks;

        Animation _animationUp;
        Animation _animationDie;
        Animation _animationBite;
        Animation _animationFly;
        Animation _animationOwl;
        Animation _animationOwlDrown;
        eState _state;

        public enum eState
        {
            eDisabled = 0,
            eVisible,
            eUp,
            eBite,
            eDie,
            eOwl,
            eDrown,
            eFly
        }

        private List<Vector2> _destinationPoints;
        private int _destinationPointIndex;

        #region constructors

        /// <summary>
        /// Initialize
        /// </summary>
        public void Initialize()
        {
            _spritePosition = Vector2.Zero;
            _destinationPoints = new List<Vector2>();
        }
        #endregion constructors

        #region properties

        public Item Item
        {
            set
            {
                _item = value;
            }

            get
            {
                return _item;
            }
        }

        public int NbClicks
        {
            set
            {
                _nbClicks = value;
            }

            get
            {
                return _nbClicks;
            }
        }

        public Vector2 Position
        {
            get
            {
                return _spritePosition;
            }
            set
            {
                _spritePosition = value;
            }
        }
        public Pair ItemPosition
        {
            get
            {
                return _ItemPosition;
            }
            set
            {
                _ItemPosition = value;
            }
        }
        public eState State
        {
            get
            {
                return _state;
            }
            set
            {
                _state = value;
            }
        }

        public float Height
        {
            get
            {
                return _height;
            }
        }

        public float Width
        {
            get
            {
                return _width;
            }
        }

        public bool IsAlive
        {
            get
            {
                return _state != eState.eDie;
            }
        }

        #endregion properties


        public void Update(GameTime gameTime)
        {
            switch (_state)
            {
                case eState.eUp:
                    _spritePosition.Y -= gameTime.ElapsedGameTime.Milliseconds / 100.0f * 4.0f;
                    break;
                case eState.eDie:
                    _spritePosition.Y += gameTime.ElapsedGameTime.Milliseconds / 100.0f * 15.0f;
                    break;
                case eState.eFly:
                    Vector2 destinationPoint = _destinationPoints[_destinationPointIndex];
                   _spritePosition = Vector2.Lerp(_spritePosition, destinationPoint, gameTime.ElapsedGameTime.Milliseconds/1000.0f * 4.0f);

                    if (_destinationPointIndex == 2 && (destinationPoint - _spritePosition).Length() < 0.5f)
                    {
                        _spritePosition = _destinationPoints[2];
                        this.SetState(eState.eBite);
                    }
                    else if (_destinationPointIndex<2 && (destinationPoint - _spritePosition).Length() < 10.0f)
                    {
                        _destinationPointIndex++;
                    }
                    break;
            }
            
        }

        public void LoadTextures(Microsoft.Xna.Framework.Content.ContentManager content)
        {
            _textureItem = content.Load<Texture2D>(GameParameters.BEAVER_TEXTURE_NAME);
            _width = _textureItem.Width;
            _height = _textureItem.Height;
            _spriteOffset.X = _width * 0.5f;
            _spriteOffset.Y = _height * 0.5f;
            
            _animationUp = AnimationManager.Instance.PlayAnimation(AnimationManager.eAnimation.AnimationUp);
            _animationBite = AnimationManager.Instance.PlayAnimation(AnimationManager.eAnimation.AnimationBite);
            _animationDie = AnimationManager.Instance.PlayAnimation(AnimationManager.eAnimation.AnimationDie);
            _animationOwl = AnimationManager.Instance.PlayAnimation(AnimationManager.eAnimation.AnimationOwl);
            _animationOwlDrown = AnimationManager.Instance.PlayAnimation(AnimationManager.eAnimation.AnimationOwlGlasses);
            _animationFly = AnimationManager.Instance.PlayAnimation(AnimationManager.eAnimation.AnimationFly);
        }

        public void Draw(Microsoft.Xna.Framework.Graphics.SpriteBatch batch)
        {
            Color color = Color.White;
            Vector2 pos = GameParameters.SCROLL_OFFSET + _spritePosition - _spriteOffset;
            //batch.Draw(_textureItem, pos, color);

            switch (_state)
            {
                case eState.eUp:
                    _animationUp.SpritePosition = _spritePosition;
                    break;
                case eState.eBite:
                    _animationBite.SpritePosition = _spritePosition;
                    break;
                case eState.eDie:
                    _animationDie.SpritePosition = _spritePosition;
                    break;
                case eState.eFly:
                    _animationFly.SpritePosition = _spritePosition;
                    break;
                case eState.eDrown:
                case eState.eOwl:
                    _animationOwl.SpritePosition = -GameParameters.SCROLL_OFFSET + GameParameters.OWL_POSITION;
                    _animationOwlDrown.SpritePosition = -GameParameters.SCROLL_OFFSET + GameParameters.OWL_POSITION;
                    break;
            }

            if (_line == null)
            {
                _line = new Texture2D(batch.GraphicsDevice, 1, 1);
                _line.SetData(new Color[] { Color.Blue });
            }
            //batch.Draw(_line, new Rectangle((int)_spritePosition.X - _animationBite.Texture[0].Width / 2, (int)_spritePosition.Y+20, 50, 1), Color.Blue);
        }

        public void FlyFromTo(Vector2 from, Vector2 to)
        {
            _destinationPoints.Clear();
            SetState(eState.eFly);
            _spritePosition = from;

            _destinationPointIndex = 0;
            //_destinationPoints.Add(genControlPoint(_spritePosition, to));
            //_destinationPoints.Add(genControlPoint(to, _spritePosition));

            Vector2 point = _spritePosition + (to - _spritePosition) * 1 / 3;
            point.Y -= 50;
            _destinationPoints.Add(point);

            point = _spritePosition + (to - _spritePosition) * 2 / 3;
            point.Y -= 50;
            _destinationPoints.Add(point);
            /*
            
            _controlPoint2 = _spritePosition + (_spritePositionDestination - _spritePosition) * 2 / 3;
            _controlPoint2.Y -= 50;*/

            _destinationPoints.Add(to);
        }

        private Vector2 BezierInterpolate(Vector2 p0, Vector2 p1, Vector2 c0, Vector2 c1, float fraction)
        {
            // first stage, linear interpolate point pairs: [p0, c0], [c0, c1], [c1, p1]
            Vector2 p0c0 = Vector2.Lerp(p0, c0, fraction);
            Vector2 c0c1 = Vector2.Lerp(c0, c1, fraction);
            Vector2 c1p1 = Vector2.Lerp(c1, p1, fraction);

            // second stage, reduce to two points
            Vector2 l = Vector2.Lerp(p0c0, c0c1, fraction);
            Vector2 r = Vector2.Lerp(c0c1, c1p1, fraction);

            // final stage, reduce to result point and return
            return Vector2.Lerp(l, r, fraction);
        }

        private Vector2 genControlPoint(Vector2 l, Vector2 r)
        {
            // get a random angle between +/-(15..90) degrees off line
            float angle = (float)((((randomGenerator.NextDouble() * 5) + 1) / 12) * Math.PI * (randomGenerator.Next(0, 2) * 2 - 1));

            // create rotation matrix
            Matrix rot = Matrix.CreateRotationZ(angle);

            // get point offset half-way between two points
            //Vector2 ofs = Vector2.op_Multiply(Vector2.op_Subtract(l, r), 0.5);
            Vector2 ofs = new Vector2(1 - r.X, r.Y) * 0.5f;
            // apply rotation
            ofs = Vector2.Transform(ofs, rot);

            // return point as control
            return ofs + new Vector2(1, 0); // Vector2.op_Add(l, ofs);
        }


        public void SetState(eState state)
        {
            _state = state;
            switch (_state)
            {
                case eState.eUp:
                    _animationBite.State = Animation.eState.eDisable;
                    _animationUp.State = Animation.eState.ePlaying;
                    _animationDie.State = Animation.eState.eDisable;
                    _animationOwl.State = Animation.eState.eDisable;
                    _animationOwlDrown.State = Animation.eState.eDisable;
                    _animationFly.State = Animation.eState.eDisable;
                    break;
                case eState.eBite:
                    _animationBite.State = Animation.eState.ePlaying;
                    _animationUp.State = Animation.eState.eDisable;
                    _animationDie.State = Animation.eState.eDisable;
                    _animationOwl.State = Animation.eState.eDisable;
                    _animationOwlDrown.State = Animation.eState.eDisable;
                    _animationFly.State = Animation.eState.eDisable;
                    break;
                case eState.eDie:
                    _animationBite.State = Animation.eState.eDisable;
                    _animationUp.State = Animation.eState.eDisable;
                    _animationDie.State = Animation.eState.ePlaying;
                    _animationOwl.State = Animation.eState.eDisable;
                    _animationOwlDrown.State = Animation.eState.eDisable;
                    _animationFly.State = Animation.eState.eDisable;
                    break;
                case eState.eFly:
                    _animationBite.State = Animation.eState.eDisable;
                    _animationUp.State = Animation.eState.eDisable;
                    _animationDie.State = Animation.eState.eDisable;
                    _animationFly.State = Animation.eState.ePlaying;
                    _animationOwl.State = Animation.eState.eDisable;
                    _animationOwlDrown.State = Animation.eState.eDisable;
                    break;
                case eState.eOwl:
                    _animationBite.State = Animation.eState.eDisable;
                    _animationUp.State = Animation.eState.eDisable;
                    _animationDie.State = Animation.eState.eDisable;
                    _animationOwl.State = Animation.eState.ePlaying;
                    _animationOwlDrown.State = Animation.eState.eDisable;
                    _animationFly.State = Animation.eState.eDisable;
                    break;
                case eState.eDrown:
                    _animationBite.State = Animation.eState.eDisable;
                    _animationUp.State = Animation.eState.eDisable;
                    _animationDie.State = Animation.eState.eDisable;
                    _animationOwl.State = Animation.eState.eDisable;
                    _animationOwlDrown.State = Animation.eState.ePlaying;
                    _animationFly.State = Animation.eState.eDisable;
                    break;
                case eState.eDisabled:
                    _animationBite.State = Animation.eState.eDisable;
                    _animationUp.State = Animation.eState.eDisable;
                    _animationDie.State = Animation.eState.eDisable;
                    _animationFly.State = Animation.eState.eDisable;
                    break;
            }
        }

        public void OnClick()
        {
            if (_state != eState.eFly)
            {
                _nbClicks--;
            }
        }
    }
}
